package cn.tedu.charging.device.dao.repository.impl;

import cn.tedu.charging.device.dao.repository.StationCacheRepository;
import cn.tedu.charging.device.pojo.po.StationPO;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.geo.*;
import org.springframework.data.redis.connection.RedisGeoCommands;
import org.springframework.data.redis.core.BoundGeoOperations;
import org.springframework.data.redis.core.HashOperations;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.ValueOperations;
import org.springframework.stereotype.Repository;

import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

@Repository("CacheGEOANDNoShareHash")
@Slf4j
public class StationCacheGEOANDNoShareHashRepository implements StationCacheRepository {

    @Autowired
    private RedisTemplate redisTemplate;

    @Override
    public void saveStation2Redis(List<StationPO> stationPOList) {

        Map<Integer, Point> geoMap = new HashMap<Integer, Point>();
        for (StationPO stationPO : stationPOList) {
            // 获取经纬度
            BigDecimal stationLng = stationPO.getStationLng();
            BigDecimal stationLat = stationPO.getStationLat();
            // 转换为Double 存入Point
            Double lng = Double.valueOf(stationLng.toEngineeringString());
            log.debug("经度{}",lng);
            Double lat = Double.valueOf(stationLat.toEngineeringString());
            log.debug("纬度{}",lat);

            Point point = new Point(lng, lat);
            geoMap.put(stationPO.getId(), point);
            // 外层 Key
            String bigKey = "station_info_hash_" + stationPO.getId();
            // 外层 value 内层的小Map
            Map<String,Object> smallMap = new HashMap<>();
            // 内层 map 的K V 为对象的字段名 和 对应的字段值
            smallMap.put("stationName",stationPO.getStationName());
            // 获取操作Hash Map 的操作对象
            HashOperations hashOperations = redisTemplate.opsForHash();
            hashOperations.putAll(bigKey,smallMap);

        }
        //定义操作geo的 operations 同时绑定key  stations
        BoundGeoOperations stations = redisTemplate.boundGeoOps("stations_geo");
        //批量保存 位置信息 point  和 场站信息 stationPO 到Redis-GEO
        stations.add(geoMap);

    }

    @Override
    public List<StationPO> search(Double longitude, Double latitude, Double distance) {
        log.debug("开始根据经纬度{},{},和距离{}进行搜索",longitude,latitude,distance);
        // 定义geo 的 operations 绑定key stations
        BoundGeoOperations stations = redisTemplate.boundGeoOps("stations_geo");
        HashOperations hashOperations = redisTemplate.opsForHash();
        log.debug("获取stations{}",stations);
        // 定义一个圆 即规定的范围 从范围中获取具体的点的坐标，列表数据，从而获取附近的场站信息点的位置
        Circle circle = getCircle(longitude, latitude, distance);
        log.debug("获取circle{},根据参数",circle);
        // 查询 通过circle
        log.debug("通过RedisGEO Circle:{} 查询附近充电站",circle);
        GeoResults<RedisGeoCommands.GeoLocation<Integer>> results = stations.radius(circle);
        log.debug("通过RedisGEO Circle:{} 查询附近充电站:{}",circle,results);
        List<StationPO> stationPOS= new ArrayList<>();
        for (GeoResult<RedisGeoCommands.GeoLocation<Integer>> result : results) {
            // GeoResults 里的具体内容 用 content表示getContent() 获取里面存入的内容
            RedisGeoCommands.GeoLocation<Integer> stationPOGeoLocation = result.getContent();
            // 获取到对应的场站信息 name 是存入的参数中的 member
            Integer stationId = stationPOGeoLocation.getName();
            // 获取到对应的场站信息
            // String bigKey = "station_info_hash_" + stationPO.getId();
            // 定义大map的key
            String bigKey = "station_info_hash_" + stationId;
            // 获取大Map 对应的value 即小 map
            Map smallMap = hashOperations.entries(bigKey);
            //
            String smallMapKey = "stationName";
            String stationName = String.valueOf(smallMap.get(smallMapKey));
            // 定义对象
            StationPO stationPO = new StationPO();
            stationPO.setStationName(stationName);

            // 将场站信息存入到集合中
            stationPOS.add(stationPO);
            log.debug("获取到场站信息{}",stationPO);
        }

        return stationPOS;
    }

    private Circle getCircle(Double longitude, Double latitude, Double distance) {
        log.debug("开始根据经纬度{},{},和距离{}进行搜索",longitude,latitude,distance);
        Point point = new Point(longitude, latitude);
        Distance geoDistance = new Distance(distance, Metrics.KILOMETERS);
        Circle circle = new Circle(point, geoDistance);
        log.debug("获取圆{}",circle);
        return circle;
    }
}
